#ifndef MATH_GEOMETRY_H
#define MATH_GEOMETRY_H

#include "math/types.h"

namespace math{

enum GeometricRelation {
    PARALLEL,       //line-line, line-plane
    COINCIDENT,     //point-line, line-line, line-plane, plane-plane
    PERPENDICULAR,  //line-line, line-plane, plane-plane
    SKEW,           //line-line
    INTERSECT,      //line-line, line-plane, plane-plane
    COPLANAR        //point-plane, line-plane, plane-plane
};
 
enum ToPlaneRelation {
    POSITIVE_SIDE = 0x00000001,   // point is in the positive normal direction from the plane
    NEGATIVE_SIDE = 0x00000002,   // point is in the negative normal direction from the plane
    ON_PLANE      = 0x00000000,   // point lies on the plane
    SPANNING      = 0x00000003    // 
};
 

struct Vertex {
    Vertex() : point_(0,0,0) {}
    Vertex(const float & x, const float & y, const float & z) : point_(x,y,z) {}
    Vertex(const Vector3 & point) : point_(point) {}

    Vector3 point_;
};

struct Ray{
public:
    Ray(const Vector3 & origin, const Vector3 & direction) : origin_(origin), direction_(direction){}
    const Vector3 & GetOrigin() const {return origin_;}
    const Vector3 & GetDirection() const {return direction_;}
private:
    Ray();
    const Vector3 & origin_;
    const Vector3 & direction_;
};
 
struct Line {
public:
    Line(const Vector3 & point, const Vector3 & slope) : point_(point), slope_(slope){}
     
    const Vector3 & GetPoint() const {return point_;}
    const Vector3 & GetSlope() const {return slope_;}

    //// Calculate Distance to a point.
    //// given: 1) a line with point a, and slope v
    ////        2) a point p
    //// then: distance = | (p-a) x v | / |v|
    //float DistanceTo(const Vector3 & point) {
    //    Vector3 diff = point - GetPoint();
    //    Vector3 cross = cml::cross(diff, GetSlope());
    //    
    //    //at this point we need to divide by the length of the slope vector
    //    //but slope is a unit vector. thus its length is 1.
    //    //so skip the division step
    //    return cross.length();
    //}

    //// Calculate shortest distance between two lines.
    //// given: 1) a line with point p0, and slope v0
    ////        2) a line with point p1, and slope v1
    //// then: distance = | (p1 - p0) x (v1 x v0) | / | v1 x v0 |
    //float DistanceTo(const Line & line){
    //    Vector3 diff = line.GetPoint() - GetPoint();
    //    
    //    Vector3 denominator = cml::cross(line.GetSlope(), GetSlope());
    //    float denominator_length = denominator.length();

    //    if(denominator_length < math::kTolerance)
    //        return 0.0f;

    //    Vector3 numerator = cml::cross(diff, denominator);
    //    float numerator_length = numerator.length();

    //    return numerator_length / denominator_length;
    //}

private:
    const Vector3 & point_;
    const Vector3 & slope_;
};
 
struct Segment {
    Segment(const Vector3 & p0, const Vector3 & p1) : p0_(p0), p1_(p1) {}
     
    const Vector3 & GetP0() const {return p0_;}
    const Vector3 & GetP1() const {return p1_;}
    Vector3 GetSlope() const {return p1_ - p0_;}
private:
    Segment(); 
    const Vector3 & p0_;
    const Vector3 & p1_;
};
 
struct Triangle {
    Triangle(): p0_(NULL), p1_(NULL), p2_(NULL), normal_(0.0f, 0.0f, 0.0f){}
    Triangle(const Vertex * p0, const Vertex * p1, const Vertex * p2) : p0_(p0), p1_(p1), p2_(p2) {
        if(p0_ != NULL && p1_ != NULL && p2_ != NULL) {
            normal_ = cml::cross(p1_->point_ - p0_->point_ , p2_->point_ - p0_->point_);
            normal_.normalize();
        }
    }
    Triangle(const Vertex * p0, const Vertex * p1, const Vertex * p2, const Vector3 & normal)
        : p0_(p0), p1_(p1), p2_(p2), normal_(cml::normalize(normal)) {}

    
    const Vertex * GetP0()    const {return p0_;}
    const Vertex * GetP1()    const {return p1_;}
    const Vertex * GetP2()    const {return p2_;}
    const Vector3 & GetNormal()const {return normal_;}

    ToPlaneRelation QualifyPoint(const Vector3 & point) const;
    ToPlaneRelation QualifyTriangle(const Triangle & triangle) const;
private:

    Vertex const * p0_, * p1_, * p2_;
    Vector3 normal_;

};
  
struct Plane {
public:  
    Plane(): point_(NULL), normal_(0.0f,0.0f,0.0f) {}
    Plane(const Vertex * &point, const Vector3 &normal): point_(point), normal_(cml::normalize(normal)) {}
    Plane(const Vertex * &point, const Vector3 & v0, const Vector3 & v1) 
        : point_(point), normal_(cml::normalize(cml::cross(v0, v1))) {}
    
    static Plane FromPoints(const Vertex * p0, const Vertex *p1, const Vertex *p2) {        
        Vector3 v0, v1;

        if(p0 != NULL && p1 != NULL && p2 != NULL) {
            v0 = p1->point_ - p0->point_;
            v1 = p2->point_ - p0->point_;
        }

        return Plane(p0, v0, v1);
    }
    
    Vertex const * GetPoint() const {return point_;}
    const Vector3 & GetNormal() const {return normal_;}

    void SetPoint(const Vertex * &point) {point_ = point;}
    void SetNormal(const Vector3 & normal) {normal_ = normal;}

    const Plane & operator =(const Plane & other){
        point_ = other.GetPoint();
        normal_ = other.GetNormal();
    }
    const Plane & operator =(const Triangle & other){
        point_ = other.GetP0();
        normal_ = other.GetNormal();
    }

    ToPlaneRelation QualifyPoint(const Vector3 & point) const;
    ToPlaneRelation QualifyTriangle(const Triangle & triangle) const;
private:
    Vertex const * point_;
    Vector3 normal_;
};


 





}//namespace math

#endif //MATH_GEOMETRY_H